/* created at 2008-07-02:23:21:53 */
package pl.kwiecienm.jcomet;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import pl.kwiecienm.jcomet.configuration.CometConfiguration;
import pl.kwiecienm.jcomet.model.AnalysisResult;
import pl.kwiecienm.jcomet.output.MultipleResultWritter;
import pl.kwiecienm.jcomet.output.SingleResultWritter;
import pl.kwiecienm.jcomet.output.Writter;

/**
 * @author kwiecienm
 */
public class Comet {

    /** */
    private Log                log = LogFactory.getLog(this.getClass());
    /** */
    private CometConfiguration _cometConfiguration;
    /** */
    private FileAnalyzer       _fileAnalyzer;

    /**
     * @param inRootSrcFolder
     */
    public Comet(CometConfiguration inCometConfiguration) {
        this._cometConfiguration = inCometConfiguration;
        this._fileAnalyzer = new FileAnalyzer();
    }

    /** */
    public Map<String, Object> doMetrics() {
        List<AnalysisResult> analysisResultList = new ArrayList<AnalysisResult>();
        Map<String, AnalysisResult> calculationsMap = new HashMap<String, AnalysisResult>();
        Map<String, Collection<AnalysisResult>> aggreagationsMap = new HashMap<String, Collection<AnalysisResult>>();
        Map<String, Object> returnMap = new HashMap<String, Object>();

        initializeFileAnalyzer();

        doAnalysis(analysisResultList);

        performAggregationsAndCalculations(analysisResultList,
                aggreagationsMap, calculationsMap, returnMap);

        writeOuput(returnMap);

        return returnMap;
    }

    /**
     * @param inReturnMap
     */
    private void writeOuput(Map<String, Object> inReturnMap) {
        log.info("Writing the output");

        if (this._cometConfiguration.getWritters() != null) {
            for (Writter w : this._cometConfiguration.getWritters()) {
                processWritter(w, inReturnMap);
            }
        }
    }

    /**
     * @param inAnalysisResultList
     * @param inCalculationsMap
     * @param inAggreagationsMap
     * @param inReturnMap
     */
    private void performAggregationsAndCalculations(
            List<AnalysisResult> inAnalysisResultList,
            Map<String, Collection<AnalysisResult>> inAggreagationsMap,
            Map<String, AnalysisResult> inCalculationsMap,
            Map<String, Object> inReturnMap) {

        log.info("Performing calculation on analysis results");

        if (this._cometConfiguration.getAggregators() != null) {
            for (Aggregator aggregator : this._cometConfiguration
                    .getAggregators()) {
                inAggreagationsMap.put(aggregator.getName(), aggregator
                        .aggregate(inAnalysisResultList));
            }
        }
        if (this._cometConfiguration.getCalculations() != null) {
            for (Calculation calculation : this._cometConfiguration
                    .getCalculations()) {
                inCalculationsMap.put(calculation.getName(), calculation
                        .calculate(inAnalysisResultList));
            }
        }
        inReturnMap.putAll(inAggreagationsMap);
        inReturnMap.putAll(inCalculationsMap);
    }

    /**
     * @param inAnalysisResultList
     */
    private void doAnalysis(List<AnalysisResult> inAnalysisResultList) {
        log.info("Analysing the files");

        if (this._cometConfiguration.getSourceFolders() != null) {
            for (String fileName : this._cometConfiguration.getSourceFolders()) {
                analyse(new File(fileName), inAnalysisResultList);
            }
        }
    }

    /** */
    private void initializeFileAnalyzer() {
        log.info("Initializing analyzers");

        if (this._cometConfiguration.getAnalyzers() != null) {
            for (LineAnalyzer analyzer : this._cometConfiguration
                    .getAnalyzers()) {
                this._fileAnalyzer.addAnalyzer(analyzer);
            }
        }
    }

    /**
     * @param inWritter
     * @param inMap
     */
    @SuppressWarnings("unchecked")
    private void processWritter(Writter inWritter, Map<String, Object> inMap) {
        Object obj = inMap.get(inWritter.getRefName());
        if (obj != null) {
            if (inWritter instanceof MultipleResultWritter) {
                MultipleResultWritter mrw = (MultipleResultWritter) inWritter;
                mrw.write((Collection<AnalysisResult>) obj);
            } else {
                SingleResultWritter mrw = (SingleResultWritter) inWritter;
                mrw.write((AnalysisResult) obj);
            }
        }
    }

    /**
     * @param inFile
     * @param inAnalysisResultList
     */
    private void analyse(File inFile, List<AnalysisResult> inAnalysisResultList) {
        if (inFile.isDirectory()) {
            File[] files = inFile.listFiles();
            for (File f : files) {
                analyse(f, inAnalysisResultList);
            }
        } else if (inFile.isFile()) {
            AnalysisResult analysisResult = this._fileAnalyzer.analyse(inFile);
            inAnalysisResultList.add(analysisResult);
        }
    }
}
